package com.abhinav;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class CloneTest {
	@Test
	public void testFooClone() throws Exception {
		Foo a = new Foo();
		Foo b = a.clone();

		assertThat(a != b, is(true));
		assertThat(Foo.constructorCallsCount, is(1));
		assertThat(a.name, is("foo"));
		assertThat(Foo.constructorCallsCount, is(1)); // Note1: Wow, a new instance without calling constructor!
		assertThat(b.name, is("foo"));

		a.name = "fooX";
		assertThat(a.name, is("fooX"));
		assertThat(b.name, is("foo"));

		Foo2 c = new Foo2();
		c.ids.add("a");
		Foo2 d = c.clone();
		d.ids.add("b");

		c.ids.clear();
		c.ids.add("A");

		assertThat(c != d, is(true));
		assertThat(Foo.constructorCallsCount, is(2));
		assertThat(c.name, is("foo"));
		assertThat(Foo.constructorCallsCount, is(2));
		assertThat(d.name, is("foo"));

		assertThat(c.ids, hasItems("A"));
		assertThat(c.ids.size(), is(1));
		assertThat(d.ids, hasItems("a", "b"));
		assertThat(d.ids.size(), is(2));
	}

	/** A simple clone examples with simple type fields. */
	public static class Foo implements Cloneable {
		static int constructorCallsCount = 0;
		String name = "foo";

		public Foo() {
			constructorCallsCount++;
		}

		public Foo clone() {
			// Note2: Catch checked exception here so client or subclass doesn't need to.
			Foo result = null;
			try {
				result = (Foo) super.clone();
			} catch (CloneNotSupportedException e) {
				throw new RuntimeException("Unable to clone.", e);
			}
			return result;
		}
	}

	/** A clone example that needs to fix field afterward super.clone(). */
	public static class Foo2 extends Foo {
		List<String> ids = new ArrayList<String>();

		public Foo2 clone() {
			Foo2 result = (Foo2) super.clone(); // Note3: Some how, super clone will auto return the correct type!
			result.ids = new ArrayList<String>(ids); // Note4: need to fix non-simple field.
			return result;
		}
	}
}
